package com.linghu.study.springbootshirojwtdemo.aop.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import com.linghu.study.springbootshirojwtdemo.aop.annotation.MyLog;
import com.linghu.study.springbootshirojwtdemo.bean.LogData;
import com.linghu.study.springbootshirojwtdemo.bean.Record;
import com.linghu.study.springbootshirojwtdemo.bean.User;
import com.linghu.study.springbootshirojwtdemo.mapper.LogtestMapper;
import com.linghu.study.springbootshirojwtdemo.mapper.RecordMapper;
import com.linghu.study.springbootshirojwtdemo.service.LogtestService;
import com.linghu.study.springbootshirojwtdemo.service.RecordService;
import com.linghu.study.springbootshirojwtdemo.utils.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Aspect
@Component
@Order(1)  // 当创建了多个自定义注解，并标记到同一个方法上，可以通过设置Order来指定执行顺序    // Order越小的越优先执行，但是，After方法反而越后面执行
public class DemoAspect {

    @Resource
    private LogtestMapper logtestMapper;
    @Resource
    private RecordMapper recordMapper;
    @Resource
    private RecordService recordService;
    @Resource
    private LogtestService logtestService;


    //指定切入点：AopController包下的所有方法  //  com.chenjiajin.controller.AopController
    //@Pointcut("execution(public * com.chenjiajin.controller.*Controller.*(..))")      // 表示路径下的匹配生效
    @Pointcut("@annotation(com.linghu.study.springbootshirojwtdemo.aop.annotation.MyLog)")                          // 表示有该注解的就生效
    public void Pointcut() {
    }

    /**
     * 前置增强
     */
    @Before("Pointcut()")
    public void before(JoinPoint joinPoint) {
        /*
                  RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
                  //获取Session中参数
                  String str = (String) requestAttributes.getAttribute("name", RequestAttributes.SCOPE_SESSION);
                  // 等价于 String str = (String) request.getSession().getAttribute("name");

                  //获取Request Response对象
                  HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
                  HttpServletRequest response = ((ServletRequestAttributes) requestAttributes).getResponse();
         */

        // 获取请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //设置请求开始时间
        request.setAttribute("startTime", System.currentTimeMillis());
    }


    /**
     * 后置增强
     */
    //在事件通知类型中申明returning即可获取返回值
    @AfterReturning(value = "Pointcut()", returning = "returnValue")
    //@AfterReturning("Pointcut()")
    public void afterReturn(JoinPoint joinPoint, Object returnValue) {
        System.err.println("---------------------------------AfterReturning----------------------------");

        // 获取请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        System.err.println(request.getRemoteAddr());

        LogData logData = new LogData();
        Record record = new Record();//自己写的日志实体

        //1.获取切入点所在目标对象
        Object targetObj = joinPoint.getTarget();
        System.err.println("切入点所在目标对象" + targetObj.getClass().getName());
        logData.setSignatureName(targetObj.getClass().getName());

        // 2.获取切入点方法的名字
        String methodName = joinPoint.getSignature().getName();
        System.err.println("切入方法名字：" + methodName);
        logData.setMethodName(methodName);


        // 3. 获取方法上的注解
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            MyLog myLog = method.getAnnotation(MyLog.class);
            System.err.println("切入方法注解的title:" + myLog.title());
            logData.setTitle(myLog.title());
        }

        // 获取方法参数名称
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); // 参数名
        System.err.println("切入方法的所有参数名：");
        Arrays.stream(argNames).forEach(System.err::println);


        // 获取方法参数内容
        Object[] args = joinPoint.getArgs();
        List<Object> collect = Arrays.stream(args).filter(s -> !(s instanceof HttpServletRequest || s instanceof HttpServletResponse)).collect(Collectors.toList());
        Iterator<Object> iterator = collect.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println("next="+next);
        }
        logData.setArgs(JSON.toJSONString(collect));

        //加入record表
        record.setUsername(JSON.toJSONString(collect));


        for (Object o : args) {
            if (!(o instanceof HttpServletRequest)) {
                System.err.println("切入方法的参数：" + o);

            }
        }


        // 获取请求参数信息
        String paramDataJson = JSON.toJSONString(request.getParameterMap(), SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue);
        logData.setParamData(paramDataJson);

        // 设置客户端ip
        logData.setClientIp(RequestUtil.getIPAddressByRequest(request));

        // 客户端请求路径
        logData.setUri(request.getRequestURI());
        record.setUrl(request.getRequestURI());

        // 设置请求类型（json|普通请求）
        logData.setType(RequestUtil.getRequestType(request));


        // 设置请求方法
        logData.setMethod(request.getMethod());


        Object startTime = request.getAttribute("startTime");
        if (startTime instanceof Long) {
            long time = (Long) startTime;
            // 请求时间
            logData.setTime(new Timestamp(time));
            record.setTimestamp(new Timestamp(time));

            Date startDate = new Date(time);
            System.err.println("前置时间 = " + startDate.toLocaleString());
            Date endDDate = new Date();
            System.err.println("后置时间 = " + endDDate.toLocaleString());

            // 接口返回时间
            logData.setReturnTime(new Timestamp(endDDate.getTime()));
//            record.setTimestamp(new Timestamp(endDDate.getTime()));

            // 请求耗时
            logData.setTimeConsuming(Integer.parseInt(endDDate.getTime() - startDate.getTime() + ""));
            System.err.println("请求耗时 = " + (endDDate.getTime() - startDate.getTime()) + " 毫秒");
        }


        System.err.println("返回值类型: " + returnValue.getClass());
        System.err.println("返回值: " + returnValue);
        logData.setReturnData(JSON.toJSONString(returnValue));
        //
        record.setResponse(JSON.toJSONString(returnValue));

        // 响应状态码
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        int httpStatusCode = response.getStatus();
        logData.setHttpStatusCode(httpStatusCode + "");


//        logtestMapper.insert(logData);
//        recordMapper.insert(record);//
        /**
         * 日志入库行为改到Service层去做，方便异步调用添加日志
         */
        logtestService.insertLog(logData);
        recordService.insertLog(record);
    }

    /**
     * 环绕增强
     */
    //@Around(value = "Pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.err.println("---------------------------------Around---------------------------------");
        long startTime = System.currentTimeMillis();

        Object target = joinPoint.getTarget().getClass().getSimpleName();
        System.err.println("调用的控制器: " + target);


        //通过joinPoint.getArgs()获取Args参数
        Object[] args = joinPoint.getArgs();
        if (args.length != 0) {
            for (Object object : args) {
                System.err.println("传参: " + object);
                System.err.println("参数是不是HttpServletRequest: " + (object instanceof HttpServletRequest));
                System.err.println();
            }
        }

        // 通过代理类调用业务逻辑执行
        Object result = joinPoint.proceed();// 当执行完该行代码 就表示环绕的方法代码也执行完了
        System.err.println("result = " + result);  // result就是接口的返回值: PojoDemo(returnData=success)

        log.info("当前方法{}执行成功,控制器：{}, 运行时间为:{} ms", joinPoint.getSignature().getName(), joinPoint.getTarget().getClass().getName(), (System.currentTimeMillis() - startTime));
        return result;
        // 这个地方返回null 将导致AfterReturning的方法无法执行,具体是因为aop的执行顺序

    }


}

